﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using PostSharp.CodeModel;
using PostSharp.CodeModel.Collections;
using PostSharp.Collections;
using MicroContainer;

namespace MicroContainer.CompileTime.Generators
{
   internal sealed class BuildMethodGenerator : ICodeGenerator
   {
      private readonly TypeMappingDef _mappingDef;

      public BuildMethodGenerator(TypeMappingDef mappingDef)
      {
         _mappingDef = mappingDef;
      }

      public void Generate(CodeGenerationContext ctx)
      {
         string suffix = _mappingDef.UniqieId.ToString().Replace("-", "_");
         
         MethodDefDeclaration buildMethod = new MethodDefDeclaration();
         ctx.ContainerType.Methods.Add(buildMethod);
         buildMethod.Attributes = MethodAttributes.Private | MethodAttributes.Static;
         buildMethod.CallingConvention = CallingConvention.Default;
         buildMethod.Name = "Build_" + suffix;
         buildMethod.ReturnParameter = new ParameterDeclaration();
         buildMethod.ReturnParameter.Attributes = ParameterAttributes.Retval;
         buildMethod.ReturnParameter.ParameterType = ctx.ContainerType.Module.FindType(_mappingDef.From, BindingOptions.Default);
         ParameterDeclaration param = new ParameterDeclaration();         
         buildMethod.Parameters.Add(param);
         param.Name = "containerInstance";
         param.ParameterType = buildMethod.Module.FindType(typeof (object), BindingOptions.Default);
         buildMethod.MethodBody = new MethodBodyDeclaration();
         buildMethod.MethodBody.RootInstructionBlock = buildMethod.MethodBody.CreateInstructionBlock();
         buildMethod.MethodBody.MaxStack = MethodBodyDeclaration.RecomputeMaxStack;

         if (_mappingDef.Mode == InstantiationMode.Singleton)
         {
            MethodDefDeclaration createMethod = new MethodDefDeclaration();
            ctx.ContainerType.Methods.Add(createMethod);
            createMethod.Attributes = MethodAttributes.Private | MethodAttributes.Static;
            createMethod.CallingConvention = CallingConvention.Default;
            createMethod.Name = "Create_" + suffix;
            createMethod.ReturnParameter = new ParameterDeclaration();
            createMethod.ReturnParameter.Attributes = ParameterAttributes.Retval;
            createMethod.ReturnParameter.ParameterType = ctx.ContainerType.Module.FindType(_mappingDef.From, BindingOptions.Default);
            param = new ParameterDeclaration();
            createMethod.Parameters.Add(param);
            param.Name = "containerInstance";
            param.ParameterType = createMethod.Module.FindType(typeof(object), BindingOptions.Default);
            createMethod.MethodBody = new MethodBodyDeclaration();
            createMethod.MethodBody.RootInstructionBlock = createMethod.MethodBody.CreateInstructionBlock();
            createMethod.MethodBody.MaxStack = MethodBodyDeclaration.RecomputeMaxStack;

            FieldDefDeclaration singleInstanceField = new FieldDefDeclaration();
            ctx.ContainerType.Fields.Add(singleInstanceField);
            singleInstanceField.Attributes = FieldAttributes.Private;
            singleInstanceField.FieldType = createMethod.ReturnParameter.ParameterType;
            singleInstanceField.Name = "_singleInstance_" + suffix;
         }
      }      
   }
}
